0%

Python - EDA和可视化简单示例

在做竞赛时,有一个厉害的模型能让我们提高分数,但除此之外,有一个不可被忽视的工作,那就是数据预处理(preprocessing)。对数据集进行数据清洗和特征工程是一个显示数据处理功力 的工作,它费时耗力且需要经验积累。一个好的预处理会使模型的效果事半功倍。
波士顿房价数据集是回归问题的经典数据集,本文用的是13列的小数据集,用Python对其进行了一些基础的数据处理和可视化操作,简单探索了数据规律和模型的应用。

数据集导入和简单观察

导入数据集后,先大致了解数据集构成、相关信息。下面是一些常规的观察操作,可以选择需要的使用。

1
2
3
4
5
6
7
8
9
10
11
#导入必要的包
import pandas as pd # 导入Python的数据处理库pandas
import seaborn as sns # 导入python高级数据可视化库seaborn
import matplotlib.pyplot as plt # 导入python绘图matplotlib
%matplotlib inline

df = pd.read_csv('boston_house_price_english.csv') #导入数据集,也可以用sklearn导入
df.head() # 前5行
df.shape # 行列数
df.info() # 特征信息
df.describe() # 数值特征的基本统计学信息,如最大值最小值方差等
特征名称及解释
  • CRIM: per capita crime rate by town 每个城镇人均犯罪率
  • ZN: proportion of residential land zoned for lots over 25,000 sq.ft. 超过25000平方英尺用地划为居住用地的百分比
  • INDUS: proportion of non-retail business acres per town 非零售商用地百分比
  • CHAS: Charles River dummy variable (= 1 if tract bounds river; 0 otherwise) 是否靠近查尔斯河
  • NOX: nitric oxides concentration (parts per 10 million) 氮氧化物浓度
  • RM: average number of rooms per dwelling 住宅平均房间数目
  • AGE: proportion of owner-occupied units built prior to 1940 1940年前建成自用单位比例
  • DIS: weighted distances to five Boston employment centres 到5个波士顿就业服务中心的加权距离
  • RAD: index of accessibility to radial highways 无障碍径向高速公路指数
  • TAX: full-value property-tax rate per $10,000 每万元物业税率
  • PTRATIO: pupil-teacher ratio by town 小学师生比例
  • B: 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town 黑人比例指数
  • LSTAT: % lower status of the population 下层经济阶层比例
  • MEDV: Median value of owner-occupied homes in $1000’s 业主自住房屋中值

数据探索性分析EDA和可视化

查看缺失值
1
df.isnull().any().sum()		# 看是否有缺失值

输出是0,这份小的数据集是没有缺失值的。
当出现缺失值时,先观察缺失值比例,占比较大的列可以考虑删去。缺失值一般以三种形式存在:NA/NULL/‘ ‘。重要特征的缺失值有时会转化成binary处理,但只限于是否缺失为重要信息的特征。一般的缺失值填充有以下方法:

  • 数值型特征:可以用 均值/最大值/最小值/众数 填充
  • 时间序列特征:例如苹果今天的价格缺失,可以用昨日的价格填充
  • 将缺失值归为一类:可以用不曾出现也不会出现的值,比如:缺失年龄用 999 填充,缺失体重用 -1 填充
  • 缺失比例小的连续变量:可用有数值的数据进行对缺失值的回归预测填充,例:班上某同学的身高

填充缺失值是没有固定方法的,需要根据不同的应用场景实际判断。

查看相关性
1
df.corr					# 特征之间的相关性

img02-corr

1
sns.heatmap(df.corr(),square=True,annot=True,cmap='YlGnBu') # 也可以用热力图表示

img01-heatmap

可以发现几个特征之间的相关性特别大,在这个时候,数据的清洗和预处理就更加重要了。由于最后一个变量是房屋的均价,可以把它单独拿出来和其他各个变量做特征,当然也可以在一开始的时候直接使用sns.pairplot(df)来看每个变量之间的相互关系。

1
2
3
4
5
6
7
8
9
10
11
12
plt.style.use({'figure.figsize':(20,25)})
i = 0
for each in df.columns:
plt.subplot(5,4,(i+1))
plt.scatter(df[each],df['MEDV'])
plt.title(' {} and House price'.format(each))
plt.xlabel(each)
plt.ylabel('House Price')
plt.yticks(range(0,60,5))
plt.grid()
i=i+1
plt.show()

img03-plt
img04-plt

用sklearn筛选重要特征
1
2
3
4
5
6
7
8
9
10
drop_columns = ['MEDV']
x = df.drop(drop_columns, axis=1) # 手动划分训练集
y = df['MEDV']
# 用sklearn筛选特征观察
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_regression # 用回归评测指标
SelectKBest = SelectKBest(f_regression, k=3) # 取前三最相关
bestFeature = SelectKBest.fit_transform(x,y)
SelectKBest.get_support()
x.columns[SelectKBest.get_support()]

SelectKBest的官方源码

输出为Index(['RM', 'PTRATIO', 'LSTAT'], dtype='object')。此时,可以单独对这几个变量做特征,具体做法因项目和实际需要而定,这里不做具体展开。其他用sklearn进行数据处理的方法可参考:sklearn进行特征选择和数据预处理

模型应用

关于如何对特征进行整合处理,其实有很多做法,例如分箱、独热编码、归一化等等。在这里,只简单套用一下线性回归和随机森林两个模型,跑出结果。

线性回归
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 分出数据和标签
X = df.drop(['MEDV'], axis=1).values # 数据
y = df['MEDV'].values # 标签
# 调用模型
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=888)
# 确认训练集和数据集形状
X_train.shape
y_train.shape
# 实例化模型
lin_reg = LinearRegression()
# 训练
lin_reg.fit(X_train,y_train)
# 查看分数
lin_reg.score(X,y) # 0.738792

此时模型已经完成了,可以用lin_reg.coef_查看估计的线性方程系数。也可以用测试集看效果。

1
2
3
from sklearn.metrics import mean_squared_error # 调用均方误差评价指标
y_pre = lin_reg.predict(X_test) # 预测结果
mean_squared_error(y_test, y_pre) # 评价指标 19.10388
随机森林

同样用之前分割好的训练集和测试集,代码不再重写。与线性模型代码不同的是,在这里,使用网格搜索交叉验证的方式充分评估回归模型的准确性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import GridSearchCV
# 构造参数字典,让这三个参数按列表中的顺序排列组合遍历一遍
param_grid = {
'n_estimators':[5,10,20,50,100,200], # 决策树的个数
'max_depth':[3,5,7], # 最大树深,树太深会造成过拟合
'max_features':[0.6,0.7,0.8,1] # 决策划分时考虑的最大特征数
}
# 实例化随机森林
rf = RandomForestRegressor()
# 以随机森林为基础构造网格搜索
grid = GridSearchCV(rf, param_grid=param_grid, cv=3)
# 训练
grid.fit(X_train, y_train)

输出结果

1
2
3
4
5
6
7
8
9
10
11
GridSearchCV(cv=3, error_score='raise',
estimator=RandomForestRegressor(bootstrap=True, criterion='mse', max_depth=None,
max_features='auto', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,
oob_score=False, random_state=None, verbose=0, warm_start=False),
fit_params=None, iid=True, n_jobs=1,
param_grid={'n_estimators': [5, 10, 20, 50, 100, 200], 'max_depth': [3, 5, 7], 'max_features': [0.6, 0.7, 0.8, 1]},
pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
scoring=None, verbose=0)
1
2
# 查看效果最好的参数
grid.best_params_
1
{'max_depth': 7, 'max_features': 0.6, 'n_estimators': 100}
1
2
3
# 查看效果最好参数对应的模型
rf_reg = grid.best_estimator_
rf_reg
1
2
3
4
5
6
RandomForestRegressor(bootstrap=True, criterion='mse', max_depth=7,
max_features=0.6, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=100, n_jobs=None,
oob_score=False, random_state=None, verbose=0, warm_start=False)

可视化一下随机森林的预测结果

1
2
3
4
5
6
7
8
result = {"label":y_test,"prediction":rf_reg.predict(X_test)}
result = pd.DataFrame(result)
result['label'].plot(style='k.', figsize=(15,5))
result['prediction'].plot(style='r.')
# 设置可视化图表
plt.legend(fontsize=15,markerscale=3)
plt.tick_params(labelsize=25)
plt.grid()

img05-rf

1
2
3
from sklearn.metrics import mean_squared_error # 调用均方误差评价指标
MSE = mean_squared_error(y, rf_reg.predict(X))
MSE # 评价指标 4.33319

可以看出随机森林的结果比线性回归预测要好。